/* HTTP Restful API Server

   This example code is in the Public Domain (or CC0 licensed, at your option.)

   Unless required by applicable law or agreed to in writing, this
   software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
   CONDITIONS OF ANY KIND, either express or implied.
*/
#include <string.h>
#include <fcntl.h>
#include "esp_http_server.h"
#include "esp_system.h"
#include "esp_log.h"
#include "esp_vfs.h"
#include "cJSON.h"
#include "esp_spiffs.h"
#include "esp_heap_caps.h"

static const char *REST_TAG = "esp-rest";
#define REST_CHECK(a, str, goto_tag, ...)                                              \
    do                                                                                 \
    {                                                                                  \
        if (!(a))                                                                      \
        {                                                                              \
            ESP_LOGE(REST_TAG, "%s(%d): " str, __FUNCTION__, __LINE__, ##__VA_ARGS__); \
            goto goto_tag;                                                             \
        }                                                                              \
    } while (0)

#define FILE_PATH_MAX (ESP_VFS_PATH_MAX + 128)
#define SCRATCH_BUFSIZE (10240)

char* index_html_psram_buf = NULL;  // 存放index.html文件数据的psram
char* index_98b563f8_js_psram_buf = NULL;  // 存放index-98b563f8.js文件数据的psram
char* index_5d5a4579_css_psram_buf = NULL;  // 存放index-5d5a4579.css文件数据的psram
char* index_27d02f19_js_psram_buf = NULL;  // 存放index-27d02f19.js文件数据的psram
char* index_c1e8c782_css_psram_buf = NULL;  // 存放index-c1e8c782.css文件数据的psram

unsigned int index_html_psram_buf_len = 0; // 存放index.html文件数据的psram长度
unsigned int index_98b563f8_js_psram_buf_len = 0; // 存放index.html文件数据的psram长度
unsigned int index_5d5a4579_css_psram_buf_len = 0; // 存放index.html文件数据的psram长度
unsigned int index_27d02f19_js_psram_buf_len = 0; // 存放index.html文件数据的psram长度
unsigned int index_c1e8c782_css_psram_buf_len = 0; // 存放index.html文件数据的psram长度

typedef struct rest_server_context {
    char base_path[ESP_VFS_PATH_MAX + 1];
    char scratch[SCRATCH_BUFSIZE];
} rest_server_context_t;

#define CHECK_FILE_EXTENSION(filename, ext) (strcasecmp(&filename[strlen(filename) - strlen(ext)], ext) == 0)

void readSpiffsFileToPsram(const char *filepath, char** buffer, unsigned int *buffer_len) {
  // // 检查PSRAM配置情况
  // size_t psram_size = heap_caps_get_largest_free_block(MALLOC_CAP_SPIRAM);
  // if (psram_size > 0) {
  //     printf("PSRAM enabled. Largest free block: %zu bytes.\n", psram_size);
  // } else {
  //     printf("PSRAM not enabled or no free memory available in PSRAM.\n");
  // }

  // 打开文件
  FILE *file = fopen(filepath, "r");
  if (file == NULL) {
      ESP_LOGE("SPIFFS", "Failed to open file: %s", filepath);
      return;
  }

  // 获取文件大小
  fseek(file, 0, SEEK_END);
  size_t fileSize = ftell(file);
  fseek(file, 0, SEEK_SET);

  // 在 PSRAM 中分配内存
  *buffer = (char *)heap_caps_malloc(fileSize, MALLOC_CAP_SPIRAM);
  if (buffer == NULL) {
      ESP_LOGE("SPIFFS", "Failed to allocate memory in PSRAM.");
      fclose(file);
      return;
  }

  // 读取文件内容到 PSRAM 缓冲区
  size_t bytesRead = fread(*buffer, 1, fileSize, file);
  if (bytesRead!= fileSize) {
      ESP_LOGE("SPIFFS", "Failed to read complete file.");
      heap_caps_free(*buffer);
      fclose(file);
      return;
  }

  *buffer_len = (unsigned int)fileSize;

  // 处理读取到的文件内容（这里可以根据实际需求进行操作）
  // ESP_LOGI("SPIFFS", "File content from PSRAM:\n%s", buffer);
  // ESP_LOGI("SPIFFS", "File content from PSRAM:\n%s", index_html_psram_buf);

  // 释放 PSRAM 内存
  // heap_caps_free(buffer);

  // 关闭文件
  fclose(file);
}

/* Set HTTP response content type according to file extension */
static esp_err_t set_content_type_from_file(httpd_req_t *req, const char *filepath)
{
    const char *type = "text/plain";
    if (CHECK_FILE_EXTENSION(filepath, ".html")) {
        type = "text/html";
    } else if (CHECK_FILE_EXTENSION(filepath, ".js")) {
        type = "application/javascript";
    } else if (CHECK_FILE_EXTENSION(filepath, ".css")) {
        type = "text/css";
    } else if (CHECK_FILE_EXTENSION(filepath, ".png")) {
        type = "image/png";
    } else if (CHECK_FILE_EXTENSION(filepath, ".ico")) {
        type = "image/x-icon";
    } else if (CHECK_FILE_EXTENSION(filepath, ".svg")) {
        type = "text/xml";
    }
    return httpd_resp_set_type(req, type);
}

/* Send HTTP response with the contents of the requested file */
static esp_err_t rest_common_get_handler(httpd_req_t *req)
{
    ESP_LOGI(REST_TAG, "rest_common_get_handler called!: %s", req->uri);
    char filepath[FILE_PATH_MAX];

    rest_server_context_t *rest_context = (rest_server_context_t *)req->user_ctx;
    strlcpy(filepath, rest_context->base_path, sizeof(filepath));
    if (req->uri[strlen(req->uri) - 1] == '/') {
        strlcat(filepath, "/index.html", sizeof(filepath));
    } else {
        strlcat(filepath, req->uri, sizeof(filepath));
    }

    set_content_type_from_file(req, filepath);

    ESP_LOGI(REST_TAG, "filepath is: %s", filepath);

    char *chunk = rest_context->scratch;
    unsigned int bytes = 0;
    if (strcmp(filepath, "/www/index.html") == 0) {
      ESP_LOGI(REST_TAG, "index_html_psram_buf used!");
      chunk = index_html_psram_buf;
      bytes = index_html_psram_buf_len;
    } else if(strcmp(filepath, "/www/assets/index-98b563f8.js") == 0) {
      ESP_LOGI(REST_TAG, "index_98b563f8_js_psram_buf used!");
      chunk = index_98b563f8_js_psram_buf;
      bytes = index_98b563f8_js_psram_buf_len;
    } else if(strcmp(filepath, "/www/assets/index-5d5a4579.css") == 0) {
      ESP_LOGI(REST_TAG, "index_5d5a4579_css_psram_buf used!");
      chunk = index_5d5a4579_css_psram_buf;
      bytes = index_5d5a4579_css_psram_buf_len;
    } else if(strcmp(filepath, "/www/assets/index-27d02f19.js") == 0) {
      ESP_LOGI(REST_TAG, "index_27d02f19_js_psram_buf used!");
      chunk = index_27d02f19_js_psram_buf;
      bytes = index_27d02f19_js_psram_buf_len;
    } else if(strcmp(filepath, "/www/assets/index-c1e8c782.css") == 0) {
      ESP_LOGI(REST_TAG, "index_c1e8c782_css_psram_buf used!");
      chunk = index_c1e8c782_css_psram_buf;
      bytes = index_c1e8c782_css_psram_buf_len;
    }

    if (httpd_resp_send_chunk(req, chunk, bytes) != ESP_OK) {
        ESP_LOGE(REST_TAG, "File sending failed!");
        /* Abort sending file */
        httpd_resp_sendstr_chunk(req, NULL);
        /* Respond with 500 Internal Server Error */
        httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to send file");
        return ESP_FAIL;
    }

    ESP_LOGI(REST_TAG, "File sending complete");
    /* Respond with an empty chunk to signal HTTP response completion */
    httpd_resp_send_chunk(req, NULL, 0);
    return ESP_OK;
}

void read_file_data_to_psram(void) {
    const char *filepath = "/www/index.html";
    readSpiffsFileToPsram(filepath, &index_html_psram_buf, &index_html_psram_buf_len);
    ESP_LOGI(REST_TAG, "index_html_psram_buf_len is: %d", index_html_psram_buf_len);

    filepath = "/www/assets/index-98b563f8.js";
    readSpiffsFileToPsram(filepath, &index_98b563f8_js_psram_buf, &index_98b563f8_js_psram_buf_len);
    ESP_LOGI(REST_TAG, "index_98b563f8_js_psram_buf_len is: %d", index_98b563f8_js_psram_buf_len);

    filepath = "/www/assets/index-5d5a4579.css";
    readSpiffsFileToPsram(filepath, &index_5d5a4579_css_psram_buf, &index_5d5a4579_css_psram_buf_len);
    ESP_LOGI(REST_TAG, "index_5d5a4579_css_psram_buf_len is: %d", index_5d5a4579_css_psram_buf_len);

    filepath = "/www/assets/index-27d02f19.js";
    readSpiffsFileToPsram(filepath, &index_27d02f19_js_psram_buf, &index_27d02f19_js_psram_buf_len);
    ESP_LOGI(REST_TAG, "index_27d02f19_js_psram_buf_len is: %d", index_27d02f19_js_psram_buf_len);

    filepath = "/www/assets/index-c1e8c782.css";
    readSpiffsFileToPsram(filepath, &index_c1e8c782_css_psram_buf, &index_c1e8c782_css_psram_buf_len);
    ESP_LOGI(REST_TAG, "index_c1e8c782_css_psram_buf_len is: %d", index_c1e8c782_css_psram_buf_len);
}

esp_err_t start_rest_server(const char *base_path)
{
    REST_CHECK(base_path, "wrong base path", err);
    rest_server_context_t *rest_context = calloc(1, sizeof(rest_server_context_t));
    REST_CHECK(rest_context, "No memory for rest context", err);
    strlcpy(rest_context->base_path, base_path, sizeof(rest_context->base_path));

    // 读取网页相关文件
    read_file_data_to_psram();

    httpd_handle_t server = NULL;
    httpd_config_t config = HTTPD_DEFAULT_CONFIG();
    config.uri_match_fn = httpd_uri_match_wildcard;

    ESP_LOGI(REST_TAG, "Starting HTTP Server");
    REST_CHECK(httpd_start(&server, &config) == ESP_OK, "Start server failed", err_start);

    /* URI handler for getting web server files */
    httpd_uri_t common_get_uri = {
        .uri = "/*",
        .method = HTTP_GET,
        .handler = rest_common_get_handler,
        .user_ctx = rest_context
    };
    httpd_register_uri_handler(server, &common_get_uri);

    return ESP_OK;
err_start:
    free(rest_context);
err:
    return ESP_FAIL;
}
